#include <queue>
#include <functional>
#include <mutex>
#include <future>
#include <iostream>
#include <atomic>
#include <thread>
#include <condition_variable>

class myThreadPool
{
public:
    explicit myThreadPool(int num);
    ~myThreadPool() {};

    template <typename F, typename ...Args>
    auto commit(F& f, Args &&...args) -> std::future<decltype(f(args...))>;
private:
    std::vector<std::shared_ptr<std::thread>> threadVec;
    void worker(int id);

    std::queue<std::function<void()>> taskQueue;
    std::mutex taskQueueMutex;
    std::condition_variable taskQueueNotEmpty;
};

template <typename F, typename ...Args>
auto myThreadPool::commit(F& f, Args &&...args) -> std::future<decltype(f(args...))>
{
    using RetType = decltype(f(args...));
    std::lock_guard<std::mutex> lockGuard(taskQueueMutex);
    auto task = std::make_shared<std::packaged_task<RetType()>>(std::bind(f, std::forward<Args>(args)...));
    taskQueue.emplace([task] {(*task)(); });
    taskQueueNotEmpty.notify_one();
    return task->get_future();
}

void myThreadPool::worker(int id)
{
    std::cout << "worker " << id << " running!" << std::endl;
    while (1) {
        std::unique_lock<std::mutex> uniqueLock(taskQueueMutex);
        taskQueueNotEmpty.wait(uniqueLock, [this] {return !taskQueue.empty();});
        auto task = taskQueue.front();
        taskQueue.pop();
        uniqueLock.unlock();
        std::cout << "worker " << id << " run: ";
        task();
    }
    std::cout << "worker " << id << " stopped!" << std::endl;
}

myThreadPool::myThreadPool(int num)
{
    for (int i = 0; i < num ; i ++)
    {
        // std::shared_ptr<std::thread> thrd_p(new std::thread(&myThreadPool::worker, this));
        // threadVec.push_back(thrd_p);
        threadVec.push_back(std::make_shared<std::thread>(&myThreadPool::worker, this, i));
        threadVec.back()->detach();
    }
}

int hello_world_task(int i)
{
    std::cout << "task " << i << std::endl; 
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return i;
}

int main()
{
    myThreadPool thread_pool(10);
    std::this_thread::sleep_for(std::chrono::seconds(3));
    for (int i = 0; i < 100; i ++)
    {
        auto ret = thread_pool.commit(hello_world_task, i);
        // std::cout << "return: " << ret.get() << std::endl;
    }
    std::this_thread::sleep_for(std::chrono::seconds(20));

    std::cout << "program end!" << std::endl;
}
